林嶔 (Lin, Chin)
Lesson 9
ROC曲線是醫學研究中常用到比較不同生物標記篩檢(或預測)疾病能力的統計方法。
相較於邏輯斯迴歸所獲得的勝算比,ROC曲線提供了視覺化的呈現,讓資訊接收者能一目了然哪項指標預測力最佳。
– 請至這裡下載本週的範例資料
dat = read.csv("Example data.csv", header = TRUE)
head(dat)
## eGFR Disease Survival.time Death Diabetes Cancer SBP DBP
## 1 34.65379 1 0.4771037 0 0 1 121.2353 121.3079
## 2 37.21183 1 3.0704424 0 1 1 122.2000 122.6283
## 3 32.60074 1 0.2607117 1 0 0 118.9136 121.7621
## 4 29.68481 1 NA NA 0 0 118.2212 112.7043
## 5 28.35726 0 0.1681673 1 0 0 116.7469 115.7705
## 6 33.95012 1 1.2238556 0 0 0 119.9936 116.3872
## Education Income
## 1 2 0
## 2 2 0
## 3 0 0
## 4 1 0
## 5 0 0
## 6 1 0
library(pROC)
ROC1 = roc(dat[,"Disease"], dat[,"eGFR"])
plot(ROC1, col = "red")
– 函數「which.max()」可以找尋一串數列的最大值
pos = which.max(ROC1$sensitivities + ROC1$specificities)
pos
## [1] 407
– 利用索引函數看看切點是什麼
cut = ROC1$thresholds[pos]
cut
## [1] 33.40866
plot(ROC1, col = "red")
points(ROC1$specificities[pos], ROC1$sensitivities[pos], pch = 19, cex = 2)
description = paste0("cut of point: ", formatC(cut, 2, format = "f"),
" (Sens = ", formatC(ROC1$sensitivities[pos], 3, format = "f"),
" ;Spec = ", formatC(ROC1$specificities[pos], 3, format = "f"), ")")
text(ROC1$specificities[pos], ROC1$sensitivities[pos], description, pos = 1)
– 在R裡面,我們可以這樣做…
ROC1 = roc(dat[,"Disease"], dat[,"eGFR"])
ROC2 = roc(dat[,"Disease"], dat[,"SBP"])
ROC3 = roc(dat[,"Disease"], dat[,"DBP"])
plot(ROC1, col = "red")
plot(ROC2, col = "darkgreen", add = TRUE)
plot(ROC3, col = "blue", add = TRUE)
legend("bottomright", c("eGFR", "SBP", "DBP"), lty = 1, lwd = 2, col = c("red", "darkgreen", "blue"))
roc.test(ROC2, ROC1)
##
## DeLong's test for two correlated ROC curves
##
## data: ROC2 and ROC1
## Z = 2.6874, p-value = 0.007202
## alternative hypothesis: true difference in AUC is not equal to 0
## sample estimates:
## AUC of roc1 AUC of roc2
## 0.5664361 0.5504805
roc.test(ROC2, ROC3)
##
## DeLong's test for two correlated ROC curves
##
## data: ROC2 and ROC3
## Z = 2.2119, p-value = 0.02698
## alternative hypothesis: true difference in AUC is not equal to 0
## sample estimates:
## AUC of roc1 AUC of roc2
## 0.5640136 0.5372651
– 函數「glm()」使用後,也會產生預測機率用來評估個案陽性的機率,而這個預測機率的準確度可以透過ROC曲線進行評估
model1 = glm(dat[,"Disease"]~dat[,"SBP"], family = "binomial")
model2 = glm(dat[,"Disease"]~dat[,"SBP"] + factor(dat[,"Diabetes"]), family = "binomial")
model3 = glm(dat[,"Disease"]~dat[,"SBP"] + factor(dat[,"Diabetes"]) + factor(dat[,"Education"]) + factor(dat[,"Income"]), family = "binomial")
ROC1 = roc(model1$y, model1$fitted.values)
ROC2 = roc(model2$y, model2$fitted.values)
ROC3 = roc(model3$y, model3$fitted.values)
plot(ROC1, col = "red")
plot(ROC2, col = "darkgreen", add = TRUE)
plot(ROC3, col = "blue", add = TRUE)
legend("bottomright", c("Model 1", "Model 2", "Model 3"), lty = 1, lwd = 2, col = c("red", "darkgreen", "blue"))
– 請你依照下列程序畫出ROC曲線
開一個空畫布
利用函數「lines()」畫出3條ROC曲線
自行繪製座標軸
model1 = glm(dat[,"Disease"]~dat[,"SBP"], family = "binomial")
model2 = glm(dat[,"Disease"]~dat[,"SBP"] + factor(dat[,"Diabetes"]), family = "binomial")
model3 = glm(dat[,"Disease"]~dat[,"SBP"] + factor(dat[,"Diabetes"]) + factor(dat[,"Education"]) + factor(dat[,"Income"]), family = "binomial")
ROC1 = roc(model1$y, model1$fitted.values)
ROC2 = roc(model2$y, model2$fitted.values)
ROC3 = roc(model3$y, model3$fitted.values)
plot.new()
plot.window(xlim = c(0, 1), ylim = c(0, 1))
lines(ROC1$specificities, ROC1$sensitivities, col = "red")
lines(ROC2$specificities, ROC2$sensitivities, col = "darkgreen")
lines(ROC3$specificities, ROC3$sensitivities, col = "blue")
axis(1, at = c(0, 0.2, 0.4, 0.6, 0.8, 1), labels = c("0%", "20%", "40%", "60%", "80%", "100%"), pos = 0)
axis(2, at = c(0, 0.2, 0.4, 0.6, 0.8, 1), labels = c("0%", "20%", "40%", "60%", "80%", "100%"), pos = 0, las = 2)
mtext("Specificity", side = 1, line = 3, cex.lab = 1, col = "black")
mtext("Sensitivity", side = 2, line = 3, cex.lab = 1, col = "black")
– 資料量多的時候經常會遇到這樣的問題,這時候我們可能需要告訴使用者不同區域點的密度。
plot(dat[,"SBP"], dat[,"DBP"], ylab = "DBP", xlab = "SBP", main = "Scatter plot of SBP and DBP", cex = 2)
plot(dat[,"SBP"], dat[,"DBP"], ylab = "DBP", xlab = "SBP", main = "Scatter plot of SBP and DBP", pch = 19, cex = 2)
– 舉例來說,不透明的紅色的色碼為『#FF0000』或『#FF0000FF』
– 透明度50%的紅色色碼為『#FF000080』
– 透明度50%的黑色色碼為『#00000080』
x = c(1, 0, -1, 0)
y = c(0, 1, 0, -1)
plot.new()
plot.window(xlim = c(-1, 1), ylim = c(-1, 1))
points(x, y, pch = 19, cex = 2, col = c("#FF0000", "#FF0000FF", "#FF000080", "#00000080"))
rgb(1, 0, 0, 0.5)
## [1] "#FF000080"
rgb(0.7, 0.5, 0.3, 0.7)
## [1] "#B3804DB3"
plot(dat[,"SBP"], dat[,"DBP"], ylab = "DBP", xlab = "SBP", main = "Scatter plot of SBP and DBP", pch = 19, cex = 2, col = "#00000030")
smoothScatter(dat[,"SBP"], dat[,"DBP"], nrpoints = 0, ylab = "DBP", xlab = "SBP", main = "Scatter plot of SBP and DBP")
library(fields)
fudgeit <- function(){
xm <- get('xm', envir = parent.frame(1))
ym <- get('ym', envir = parent.frame(1))
z <- get('dens', envir = parent.frame(1))
colramp <- get('colramp', parent.frame(1))
image.plot(xm,ym,z, col = colramp(256), legend.only = T, add =F)
}
par(mar = c(5,4,4,5))
smoothScatter(dat[,"SBP"], dat[,"DBP"], nrpoints = 0, ylab = "DBP", xlab = "SBP", main = "Scatter plot of SBP and DBP", postPlotHook = fudgeit)
– 現在,假設你對單一色階的散布圖仍然不滿意,想要精益求精,google給了你一條明路,請參考R Scatter Plot: symbol color represents number of overlapping points
x1 <- dat[,"SBP"] # 關鍵在這
x2 <- dat[,"DBP"] # 關鍵在這
df <- data.frame(x1,x2)
## Use densCols() output to get density at each point
x <- densCols(x1,x2, colramp=colorRampPalette(c("black", "white")))
df$dens <- col2rgb(x)[1,] + 1L
## Map densities to colors
cols <- colorRampPalette(c("#000099", "#00FEFF", "#45FE4F",
"#FCFF00", "#FF9400", "#FF3100"))(256)
df$col <- cols[df$dens]
## Plot it, reordering rows so that densest points are plotted on top
plot(x2~x1, data=df[order(df$dens),], pch = 19, col = col, cex = 2, ylab = "DBP", xlab = "SBP", main = "Scatter plot of SBP and DBP") # 關鍵在這
– 真的想要畫好看的圖,Google後修改前人的程式碼最快
– 在R裡面要叫出內建資料集,可以使用函數「data()」。(許多套件在Example的部分都會先使用函數「data()」來呼叫一份內建資料)
data(iris)
head(iris)
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosa
– 我們先用2D散布圖來看看…
COLOR = as.integer(iris$Species)+1 #先根據Species分顏色,顏色代碼2在R裡面是紅色;3是綠色;4是藍色
par(mfrow = c(2, 3))
plot(iris[,"Sepal.Length"], iris[,"Sepal.Width"], pch = 19, col = COLOR)
legend("topright", levels(iris$Species), pch = 19, col = 2:4, bg = "gray90")
plot(iris[,"Sepal.Length"], iris[,"Petal.Length"], pch = 19, col = COLOR)
legend("bottomright", levels(iris$Species), pch = 19, col = 2:4, bg = "gray90")
plot(iris[,"Sepal.Length"], iris[,"Petal.Width"], pch = 19, col = COLOR)
legend("bottomright", levels(iris$Species), pch = 19, col = 2:4, bg = "gray90")
plot(iris[,"Sepal.Width"], iris[,"Petal.Length"], pch = 19, col = COLOR)
legend("topright", levels(iris$Species), pch = 19, col = 2:4, bg = "gray90")
plot(iris[,"Sepal.Width"], iris[,"Petal.Width"], pch = 19, col = COLOR)
legend("topright", levels(iris$Species), pch = 19, col = 2:4, bg = "gray90")
plot(iris[,"Petal.Length"], iris[,"Petal.Width"], pch = 19, col = COLOR)
legend("bottomright", levels(iris$Species), pch = 19, col = 2:4, bg = "gray90")
library(scatterplot3d)
scatterplot3d(x = iris[,"Sepal.Length"],
y = iris[,"Sepal.Width"],
z = iris[,"Petal.Length"],
color = COLOR, pch = 19, angle = 40, main="3D Scatterplot")
– 套件『rgl』是在R裡面最常拿來繪製3D圖形的套件,他支援了互動式的3D圖像。
library(rgl)
library(rgl)
plot3d(x = iris[,"Sepal.Length"],
y = iris[,"Sepal.Width"],
z = iris[,"Petal.Length"],
col = COLOR, size = 3, main="3D Scatterplot")
– 我們來牛刀小試一下,剛剛的圖片我們想要命令R將我們的點圈起來,我們可以使用下面程式碼…
library(rgl)
plot3d(x = iris[,"Sepal.Length"],
y = iris[,"Sepal.Width"],
z = iris[,"Petal.Length"],
col = COLOR, size = 3, main="3D Scatterplot")
VCOV = cov(iris[,c("Sepal.Length", "Sepal.Width", "Petal.Length")])
MEAN = c(mean(iris[,"Sepal.Length"]), mean(iris[,"Sepal.Width"]), mean(iris[,"Petal.Length"]))
ellips = ellipse3d(VCOV, centre = MEAN, level = 0.95)
plot3d(ellips, col = "black", alpha = 0.2, add = TRUE, box = FALSE)
You must enable Javascript to view this page properly.
plot3d(x = iris[,"Sepal.Length"],
y = iris[,"Sepal.Width"],
z = iris[,"Petal.Length"],
col = COLOR, size = 3, xlab = "Sepal Length", ylab = "Sepal Width", zlab = "Petal Length")
VCOV = cov(iris[1:50,c("Sepal.Length", "Sepal.Width", "Petal.Length")])
MEAN = apply(iris[1:50,c("Sepal.Length", "Sepal.Width", "Petal.Length")], 2, mean)
ellips <- ellipse3d(VCOV, centre = MEAN, level = 0.95)
plot3d(ellips, col = "red", alpha = 0.2, add = TRUE, box = FALSE)
VCOV = cov(iris[51:100,c("Sepal.Length", "Sepal.Width", "Petal.Length")])
MEAN = apply(iris[51:100,c("Sepal.Length", "Sepal.Width", "Petal.Length")], 2, mean)
ellips <- ellipse3d(VCOV, centre = MEAN, level = 0.95)
plot3d(ellips, col = "green", alpha = 0.2, add = TRUE, box = FALSE)
VCOV = cov(iris[101:150,c("Sepal.Length", "Sepal.Width", "Petal.Length")])
MEAN = apply(iris[101:150,c("Sepal.Length", "Sepal.Width", "Petal.Length")], 2, mean)
ellips <- ellipse3d(VCOV, centre = MEAN, level = 0.95)
plot3d(ellips, col = "blue", alpha = 0.2, add = TRUE, box = FALSE)
You must enable Javascript to view this page properly.
本次課程教了一個新的統計分析工具:ROC曲線。其實他能做到的事情『迴圈+邏輯斯迴歸』也能做到,但視覺化呈現後能讓資訊接收者更快掌握資訊,這就是資料視覺化的魅力。
另外,本次課程中同學學習到最重要的是學習如何利用Google找到與自己想做的事情相似的程式碼,並利用Google到的程式碼套用到自己的資料上。
– 如果你google的到,你甚至可以利用套件『rgl』做出gif的動畫檔案。(註:不一定要全程使用R做出來,只要能做出來就是好方法!)